﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Shlook.Client;
using Shlook.Common;
using System.IO;
using System.Reflection;

namespace Shlook.GUI
{
    public partial class ClientGUI : Form
    {
        public ClientGUI()
        {
            InitializeComponent();
        }

        private ChatClient _client;
        private Dictionary<string, User> _connectedUsers = new Dictionary<string, User>();
        private ChatConfiguration _config;
        private User _user;
        private Dictionary<string, ChatWindowForm> _chatWindows = new Dictionary<string, ChatWindowForm>();

        private void comboBoxFocusLeave(object sender, EventArgs e)
        {
            comboBoxIndexChanged(sender, e);
        }

        private void comboBoxIndexChanged(object sender, EventArgs e)
        {
            if (_client != null)
            {
                _user.Status = statusComboBox.Text;
                _client.ChangeStatus(_user.Status);
            }
        }

        private void ClientGUI_Load(object sender, EventArgs e)
        {
            InitUI();

            _config = ChatConfiguration.Load();
#if !DEBUG
            if (String.IsNullOrEmpty(_config.ClientURL))
#endif
            {
                int portNumber = 9091;
#if DEBUG
                portNumber += Environment.TickCount % 17;
#endif
                _config.ClientURL = "net.tcp://" + Environment.MachineName + ":" + portNumber + "/ChatClient";
            }
#if !DEBUG
            if (String.IsNullOrEmpty(_config.ClientDisplayName))
#endif
            {
                DisplayNameInputForm displayNameInput = new DisplayNameInputForm();
                if (displayNameInput.ShowDialog() == DialogResult.Cancel)
                {
                    Environment.Exit(0);
                }
                _config.ClientDisplayName = displayNameInput.DisplayName;
            }
            _config.Save();

            Text = "Shlook: " + _config.ClientDisplayName;

            string userId = Environment.UserName;
#if DEBUG
            userId += _config.ClientDisplayName;
#endif

            InitialConnect(userId);
        }

        private void InitUI()
        {
            Icon = IconProvider.ApplicationIcon;
            notifyIcon.Icon = Icon;

            statusComboBox.SelectedIndex = 0;
        }

        private void InitialConnect(string userId)
        {
            _user = new User { Id = userId, DisplayName = _config.ClientDisplayName, Status = statusComboBox.Text, URL = _config.ClientURL };

            _client = new ChatClient(_user.Id, _user.DisplayName, _user.Status);
            _client.UserAdded += (o, args) => this.BeginInvoke((MethodInvoker)delegate { AddUser(args.User); });
            _client.StatusChanged += (o, args) => this.BeginInvoke((MethodInvoker)delegate { ChangeUserStatus(args.User); });
            _client.MessageArrived += (o, args) => this.BeginInvoke((MethodInvoker)delegate { DisplayMessage(args.From, args.Message); });
            _client.UserRemoved += (o, args) => this.BeginInvoke((MethodInvoker)delegate { RemoveUser(args.UserId); });
            _client.ClientError += (o, args) => this.BeginInvoke((MethodInvoker)delegate { ClientError(args.Error); });

            _client.Connect(_config.ServerURL, _user.URL);

            foreach (User user in _client.GetUsers())
            {
                if (user.Id == userId)
                    continue;

                AddUser(user);
            }
        }

        private void ClientError(string errorMessage)
        {
            if (MessageBox.Show("An error occurred that might prevent you from working normally." + Environment.NewLine +
                                "Please try again; if the problem persists, exit the client and " + Environment.NewLine +
                                "reconnect after restarting it." + Environment.NewLine + Environment.NewLine +
                                "Would you like to see technical details?",
                                "An error occurred", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes)
            {
                MessageBox.Show("Technical error details: " + Environment.NewLine + errorMessage,
                    "Error Details", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void AddUser(User user)
        {
            if (_connectedUsers.ContainsKey(user.Id))
            {
                RemoveUser(user.Id);
            }

            _connectedUsers.Add(user.Id, user);
            ListViewItem userItem = usersList.Items.Add(user.DisplayName);
            userItem.Tag = user.Id;
            userItem.SubItems.Add(user.Status);

            ChatWindowForm chatWindow;
            if (_chatWindows.TryGetValue(user.Id, out chatWindow))
            {
                chatWindow.UserConnected();
            }

            usersList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        }

        private void ChangeUserStatus(User user)
        {
            if (!_connectedUsers.ContainsKey(user.Id))
                return;

            _connectedUsers[user.Id].Status = user.Status;

            ListViewItem item = (from ListViewItem lvi in usersList.Items where ((string)lvi.Tag) == user.Id select lvi).SingleOrDefault();
            if (item != null)
                item.SubItems[1].Text = user.Status;

            usersList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);

            ChatWindowForm chatWindow;
            if (_chatWindows.TryGetValue(user.Id, out chatWindow))
            {
                chatWindow.StatusChanged(user.Status);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Exit();
        }

        private void Exit()
        {
            _client.Disconnect();
            _askUserConfirmationOnExit = false;
            Close();
        }

        private bool _askUserConfirmationOnExit = true;

        protected override void OnClosing(CancelEventArgs e)
        {
            if (_askUserConfirmationOnExit)
            {
                if (MessageBox.Show("Are you sure you want to exit Shlook?", "Exit Shlook", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                {
                    e.Cancel = true;
                }
            }

            base.OnClosing(e);
        }

        private void usersList_DoubleClick(object sender, EventArgs args)
        {
            ListViewItem selected = usersList.SelectedItems.OfType<ListViewItem>().FirstOrDefault();
            if (selected == null)
                return;

            string userId = (string)selected.Tag;
            CreateChatWindow(_connectedUsers[userId]);
        }

        private ChatWindowForm CreateChatWindow(User user)
        {
            if (_chatWindows.ContainsKey(user.Id))
                return _chatWindows[user.Id];

            ChatWindowForm chatWindow = new ChatWindowForm(user);
            chatWindow.WindowClosed += (o, e) => _chatWindows.Remove(user.Id);
            chatWindow.MessageReady += (o, e) => _client.SendMessage(e.To.Id, e.Message);

            chatWindow.Show();

            _chatWindows.Add(user.Id, chatWindow);
            return chatWindow;
        }

        private void DisplayMessage(User from, string message)
        {
            ChatWindowForm chatWindow = CreateChatWindow(from);
            chatWindow.AddMessage(message);

            if (!this.ContainsFocus && (_chatWindows.Values.All(c => !c.ContainsFocus)))
            {
                notifyIcon.Tag = chatWindow;
                notifyIcon.BalloonTipClicked += (o, e) => chatWindow.ShowUp();

                notifyIcon.ShowBalloonTip(
                    5000,
                    "Message from " + from.DisplayName,
                    message.Substring(0, Math.Min(50,message.Length)),
                    ToolTipIcon.Info);
            }
        }

        private void RemoveUser(string userId)
        {
            _connectedUsers.Remove(userId);
            
            ListViewItem item = (from ListViewItem lvi in usersList.Items where ((string)lvi.Tag) == userId select lvi).SingleOrDefault();
            if (item != null)
            {
                usersList.Items.Remove(item);
            }
            
            ChatWindowForm chatWindow;
            if (_chatWindows.TryGetValue(userId, out chatWindow))
            {
                chatWindow.UserDisconnected();
            }
        }

        private void menuItemExit_Click(object sender, EventArgs e)
        {
            Exit();
        }

        private void menuItemRestore_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Normal;
            Activate();
        }

        private void notifyIcon_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Normal;
            Activate();
        }

        private void ClientGUI_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                ShowInTaskbar = false;
            }
            else if (!ShowInTaskbar)
            {
                ShowInTaskbar = true;
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Shlook - a multi-client chat system" + Environment.NewLine + Environment.NewLine +
                            "Copyright (C) 2009 Sasha Goldshtein and Dina Zeliger" + Environment.NewLine +
                            "www.codeplex.com/Shlook",
                            "About Shlook",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
        }

        private void reconnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _client.Disconnect();
            InitialConnect(_user.Id);
        }
    }
}
